package com.flex.showdoc4j.proxy;

import com.flex.showdoc4j.factory.MockUtil;

import java.lang.reflect.*;
import java.util.ArrayList;
import java.util.List;

/**
 * @author ganlt
 * @since 2019/08/01 8:44
 */
public class ServiceMockProxy<T> implements InvocationHandler {
    private Class<T> interfaceClass;
    private List<MockTypeResolver> mockTypeResolverList = new ArrayList<>();

    public Object proxy(Class<T> clazz, List<MockTypeResolver> mockTypeResolverList) {
        this.interfaceClass = clazz;
        if (mockTypeResolverList != null) {
            this.mockTypeResolverList = mockTypeResolverList;
        }
        return Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{interfaceClass}, this);
    }

    @Override
    public Object invoke(Object obj, Method method, Object[] objects) throws Throwable {
        Type returnType = method.getGenericReturnType();
        if (returnType instanceof ParameterizedType && ((ParameterizedType) returnType).getRawType() == List.class) {
            Type listType = getListType(returnType);
            List<Object> list = new ArrayList<>();
            for (int i = 0; i < 10; i++) {
                Object mock = MockUtil.mock((Class<?>) listType);
                list.add(mock);
            }
            return list;
        } else {
            Class<?> cls;
            if (returnType instanceof ParameterizedType) {
                cls = (Class<?>) ((ParameterizedType) returnType).getRawType();
            } else {
                cls = (Class<?>) returnType;
            }
            try {
                Object mock = MockUtil.mock(cls);
                return mock;
            } catch (Exception e) {
                for (MockTypeResolver reslover : mockTypeResolverList) {
                    if (cls.isAssignableFrom(reslover.getClass())) {
                        return reslover.executeor(returnType);
                    }
                }
            }
            return null;
        }

    }

    private Type getListType(Type type) {
        Type[] resultArgType = null;
        Type resultType = type;
        if (resultType instanceof ParameterizedType
                && ((ParameterizedType) resultType).getRawType() == java.util.List.class) {
            resultArgType = ParameterizedType.class.cast(resultType).getActualTypeArguments();
        }

        return (resultArgType != null && resultArgType.length > 0) ? resultArgType[0] : null;
    }

    public void setInterfaceClass(Class<T> interfaceClass) {
        this.interfaceClass = interfaceClass;
    }
}
